using System.Collections.Generic;
using OWL.Rendering.HRP;
using UnityEngine;
using UnityEngine.Rendering;
using Yoozoo.Gameplay.RTS;

namespace Yoozoo.Gameplay.City
{
    public class DecorationDrawManager : MonoBehaviour
    {
        private class DrawData
        {
            public DecorationMeshData prefabMeshData;
            public HashSet<DecorationDrawData> decorationDrawData;
        }
        
        private Dictionary<Mesh, Dictionary<Material,DecorationDrawData>> dataMap = new Dictionary<Mesh, Dictionary<Material,DecorationDrawData>>();

        private Dictionary<long, DrawData> indexToDrawDataMap = new Dictionary<long, DrawData>();
        
        //prefab中的索引,不同prefab中的位移旋转信息不同
        //key 渲染准备数据   value prefab数据的索引
        private Dictionary<DecorationDrawData, int> prefabMeshIndex = new Dictionary<DecorationDrawData, int>();

        public void Clear()
        {
            dataMap.Clear();
            indexToDrawDataMap.Clear();
            prefabMeshIndex.Clear();
        }

        private Camera camera;
        private Transform cameraTrans;
        private Vector3 lastCameraPosition;

        public void SetCamera(Camera camera)
        {
            this.camera = camera;
            cameraTrans = camera.transform;
        }

        public void UpdateVisionBound(float xMin,float xMax,float zMin,float zMax)
        {
            foreach (var item in dataMap.Values)
            {
                foreach (var item2 in item)
                {
                    item2.Value.UpdateCulling(xMin,xMax,zMin,zMax);
                }
            }
        }

        private float bufferDistance = 5f;
        private float xMin;
        private float xMax;
        private float zMin;
        private float zMax;
        
        private void Update()
        {
            if (camera)
            {
                //判断是否需要刷新
                var position = cameraTrans.position;
                if (Mathf.Abs(lastCameraPosition.x - position.x) >= bufferDistance ||
                    Mathf.Abs(lastCameraPosition.z - position.z) >= bufferDistance)
                {
                    lastCameraPosition = position;
                    var leftBottom = BattleUtils.GetGroundPositionByScreenPosition(camera,new Vector2(0,0));
                    var leftTop = BattleUtils.GetGroundPositionByScreenPosition(camera,new Vector2(0,Screen.height));
                    var rightTop = BattleUtils.GetGroundPositionByScreenPosition(camera,new Vector2(Screen.width,Screen.height));
                    xMin = leftTop.x - bufferDistance * 2;
                    xMax = rightTop.x + bufferDistance * 2;
                    zMin = leftBottom.z - bufferDistance * 2;
                    zMax = rightTop.z + bufferDistance;
                    UpdateVisionBound(xMin,xMax,zMin,zMax);
                }
            }
            Draw();
        }

        private void Draw()
        {
            if (!SystemInfo.supportsInstancing)
            {
                return;
            }
            //var manager = HRenderSettingsManager.Get();
            //var dmidata = manager.GetData<HDrawMeshInstData>();
            foreach (var map in dataMap.Values)
            {
                foreach (var data in map.Values)
                {
                    /*var gpuInstanceInfo = new DrawMeshInstInfo(data.mesh, data.subMeshIndex, data.material, 0, data.matrices, data.property, data.count);
                    dmidata.m_instInfos.Add(gpuInstanceInfo);*/
                    var castType = data.castShadow ? ShadowCastingMode.On : ShadowCastingMode.Off;
                    Graphics.DrawMeshInstanced(data.mesh, data.subMeshIndex, data.material, data.matrices,  data.inVisionCount,data.property,castType,false,LayerMask.NameToLayer("city"));
                }
            }
        }

        public void AddEntity(long index,DecorationMeshData meshData)
        {
            for (int i = 0; i < meshData.objects.Count; i++)
            {
                var obj = meshData.objects[i];
                for (int j = 0; j < obj.materials.Length; j++)
                {
                    AddDrawData(index,obj.mesh,obj.materials[j],i,meshData,obj.subMeshIndex[j],obj.castShadow);
                }
            }
        }
        
        public void RemoveEntity(long index)
        {
            if (indexToDrawDataMap.TryGetValue(index, out var drawData))
            {
                foreach (var data in drawData.decorationDrawData)
                {
                    data.RemoveEntity(index);
                }
                indexToDrawDataMap.Remove(index);
            }
        }

        private void AddDrawData(long index,Mesh mesh,Material material,int indexInMeshData,DecorationMeshData meshData,int subMeshIndex,bool castShadow)
        {
            if (!indexToDrawDataMap.TryGetValue(index,out var drawData))
            {
                drawData = new DrawData
                {
                    decorationDrawData = new HashSet<DecorationDrawData>(),
                    prefabMeshData = meshData,
                };
                indexToDrawDataMap.Add(index,drawData);
            }
            
            if (!dataMap.TryGetValue(mesh, out var map))
            {
                map = new Dictionary<Material, DecorationDrawData>();
                dataMap.Add(mesh,map);
            }

            if (!map.TryGetValue(material, out var data))
            {
                data = new DecorationDrawData(mesh,material,subMeshIndex,castShadow);
                map.Add(material,data);
            }
            prefabMeshIndex[data] = indexInMeshData;
            data.AddEntity(index);
            drawData.decorationDrawData.Add(data);
        }

        public void SetTRS(long index, Vector3 position,Vector3 eulerAngle,Vector3 scale)
        {
            if (indexToDrawDataMap.TryGetValue(index, out var drawData))
            {
                //检查是否在视野内
                var isInVision = position.x >= xMin && position.x <= xMax && position.z >= zMin && position.z <= zMax;
                var prefabMeshData = drawData.prefabMeshData;
                foreach (var item in drawData.decorationDrawData)
                {
                    var finalScale = scale;
                    var finalPosition = position;
                    var finelEulerAngle = eulerAngle;
                    //取得他在prefab中的索引
                    if (prefabMeshIndex.TryGetValue(item, out var indexInPrefab))
                    {
                        finalPosition += prefabMeshData.objects[indexInPrefab].position;
                        finelEulerAngle += prefabMeshData.objects[indexInPrefab].eulerAngle;
                        var originScale = prefabMeshData.objects[indexInPrefab].scale;
                        finalScale = new Vector3(scale.x * originScale.x,scale.y * originScale.y,scale.z * originScale.z);
                    }
                    item.SetTRS(index,finalPosition,finelEulerAngle,finalScale,isInVision);
                }
            }
        }
#if UNITY_EDITOR
        private void OnDrawGizmos()
        {
            foreach (var item in indexToDrawDataMap)
            {
                var index = item.Key;
                foreach (var drawData in item.Value.decorationDrawData)
                {
                    var position = drawData.GetPosition(index);
                    UnityEditor.Handles.Label(position,item.Key.ToString());
                    break;
                }
                
            }
        }

#endif
    }
}